﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Warensoft.Utility;
using System.IO;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Diagnostics;
using Warensoft.Socket.EventArgs;
using Warensoft.Socket.MessageDataStructs;

namespace Warensoft.Socket
{
    /// <summary>
    /// Socket监视服务器
    /// </summary>
    public abstract  class SocketServer
    {
        protected List<ClientSessionBase> userList = new List<ClientSessionBase>();
        public List<ClientSessionBase> UserList
        {
            get
            {
                return this.userList;
            }
        }
        protected Dictionary<string, Channel> channelList = new Dictionary<string, Channel>();
        
        public Dictionary<string, Channel> ChannelList
        {
            get
            {
                return this.channelList;
            }
        }

        private static Application application = new Application();
        public static Application Application
        {
            get
            {
                return application;
            }
        }
        /// <summary>
        /// 服务器要监视的IP地址
        /// </summary>
        public IPAddress IPAddress { get; set; }
        /// <summary>
        /// 服务器端口号
        /// </summary>
        public int Port { get; set; }
        public event EventHandler<MessageEventArgs> ServerEvent;
        public event EventHandler<NewSocketEventArgs> NewSocket;
        public event EventHandler NewMessage;
        public event EventHandler MessageSent;
        public event EventHandler ClientDisconnected;
        public event EventHandler<ChannelEventArgs > ChannelRemoved;
        public event EventHandler<ChannelEventArgs> ChannelCreated;
        private Thread monitorThread = null;
        private TcpListener listener = null;
        public SocketServer()
        {
           
            Channel.ChannelEmpty += new EventHandler(Channel_ChannelEmpty);
        }
        [MethodImpl (MethodImplOptions.Synchronized )]
        void Channel_ChannelEmpty(object sender, System. EventArgs e)
        {
            var channel = sender as Channel;
            this.ChannelList.Remove(channel.Name);
            if (this.ChannelRemoved != null)
            {
                this.ChannelRemoved(this, new ChannelEventArgs() { ChannelName = channel.Name });
            }
            channel.Dispose();
            channel = null;
            GC.Collect();
           
        }
        protected void AppendServerEvent(string message)
        {
            if (this.ServerEvent !=null )
            {
                this.ServerEvent(this, new MessageEventArgs() { Message = message });
            }
        }
       

        /// <summary>
        /// 启动监视
        /// </summary>
        public void Start()
        {
            try
            {
                this.OnStart();
                var startCondations = this.innerCheckStartCondations();
                if (startCondations.OperationDone == false)
                {
                    if (this.ServerEvent != null)
                    {
                        this.ServerEvent(this, new MessageEventArgs()
                        {
                            Message = startCondations.Message
                        });
                    }
                }

                this.monitorThread = new Thread
                    (
                    () =>
                    {
                        this.listener = new TcpListener(this.IPAddress, this.Port);
                        this.listener.Start();
                        while (true)
                        {
                            var socket = this.listener.AcceptSocket();
                            ClientSocket s = new ClientSocket(socket);

                            s.Server = this;

                            var clientSession = this.OnNewSession(s);

                            clientSession.NewMessage += new EventHandler(clientSession_NewMessage);
                            clientSession.MessageSent += new EventHandler(clientSession_MessageSent);
                            clientSession.Disconnected += new EventHandler<DisconnectedEventArgs>(clientSession_Disconnected);
                            clientSession.OnError += new EventHandler<ErrorEventArgs>(clientSession_OnError);
                            clientSession.Application = Application;
                            //this.AddOrRemoveSession(clientSession, SessionListOption.Add);
                            this.userList.Add(clientSession);
                            if (this.NewSocket != null)
                            {
                                this.NewSocket(this, new NewSocketEventArgs()
                                {
                                    ClientSocket = s
                                });
                            }
                            clientSession.StartReceive();
                        }
                    }

                    );
                this.monitorThread.Start();
                OnServerEvent("the server is started");
            }
            catch (Exception ex)
            {
                this.OnError("Warensoft Socket Server Start",ex);
            }
        }

        private void OnServerEvent(string message)
        {
            if (this.ServerEvent != null)
            {
                this.ServerEvent(this, new MessageEventArgs()
                {
                    Message = message 
                });
            }
        }
        EventLog clientSessionLog = new EventLog();
        void clientSession_OnError(object sender,ErrorEventArgs e)
        {
            this.OnError("Warensoft.Socket SessionClient Error",e.GetException ());
           
        }
        public void OnError(string source,Exception ex)
        {
            if (EventLog.SourceExists(source) == false)
            {
                EventLog.CreateEventSource(source, "Warensoft Socket Server Log" );
            }
            this.clientSessionLog.Source = source ;
            this.clientSessionLog.WriteEntry(ex.Message + "\n" + ex.StackTrace, EventLogEntryType.Error);
            this.OnServerEvent("system error:"+ex.Message +"\n"+ex.StackTrace );
        }
        [MethodImpl(MethodImplOptions.Synchronized)]
        void clientSession_Disconnected(object sender, DisconnectedEventArgs  e)
        {
            var client = sender as ClientSessionBase;

            //this.removecount++;
            //Trace.WriteLine("rest session count " + removecount);
            Trace.WriteLine("disconnected count "+e.CommandArgument );
            
            this.AddOrRemoveSession(client,SessionListOption .Remove );
        }
        //private int addcount = 0;
        //private int removecount = 0;
        //private int removecount1 = 0;
        [MethodImpl(MethodImplOptions.Synchronized)]
        internal void AddOrRemoveSession(ClientSessionBase client,SessionListOption option)
        {
            //Trace.WriteLine("session count  " + this.userList.Count());
            if (option ==SessionListOption .Add )
            {
               // addcount++;
                //Trace.WriteLine("session count "+addcount );
               // Trace.WriteLine("session count before add " + this.userList.Count);
               // Trace.WriteLine(client .Name +" added");
                this.userList.Add(client);
                Trace.WriteLine("session count after add " + this.userList.Count);
                
            }
            if (option ==SessionListOption .Remove )
            {
                if (this.ClientDisconnected != null)
                {
                    this.ClientDisconnected(client ,new System.EventArgs ());
                }
               // this.removecount1 ++;
               // Trace.WriteLine("remove count1 "+this.removecount1 );
                //Trace.WriteLine("session count before remove "+this.userList .Count );
               // Trace.WriteLine(client.Name +" removed");
                this.userList.Remove(client);
                Trace.WriteLine("session count after remove " + this.userList.Count);
                client.Dispose();
                client = null;
                GC.Collect();
                //Trace.WriteLine("session count:" + this.userList.Count());
            }
            
        }
        void clientSession_MessageSent(object sender, System.EventArgs e)
        { 
            if (this.MessageSent  !=null )
            {
                this.MessageSent(this, e);
            }   
        }

        void clientSession_NewMessage(object sender, System.EventArgs e)
        {
            if (this.NewMessage != null)
            {
                this.NewMessage(this, e);
            }
        }

       

      
        /// <summary>
        /// 关闭监视
        /// </summary>
        public void Stop()
        {
            this.OnStop();
            this.listener.Stop();
            this.monitorThread.Abort();
            this.userList.Clear();
            this.channelList.Clear();
            GC.Collect();
            this.OnServerEvent("the server is stopped");
        }
        public abstract ClientSessionBase  OnNewSession(ClientSocket socket);
        /// <summary>
        /// 检测启动条件
        /// </summary>
        /// <returns></returns>
        private ResultInfo innerCheckStartCondations()
        {
            if (this.IPAddress ==null||this.IPAddress ==IPAddress .None )
            {
                return new ResultInfo() {  OperationDone =false ,Message ="请配置服务器IP"};
            }
            if (this.Port ==0)
            {
                return new ResultInfo() { OperationDone =false ,Message ="请配置服务器端口"};
            }
            return this.CheckStartCondations();
        }
        public abstract  ResultInfo CheckStartCondations();
        /// <summary>
        /// 对指定用户进行广播
        /// </summary>
        /// <param name="message">要广播的消息</param>
        /// <param name="userIDs">客户ID数组</param>
        public void BroadcastForUsers(PackageBase message, params string[] userNames)
        {
            var users = this.userList.Where(user =>
                {
                    return userNames.Contains(user.Name);
                }
            );
            foreach (var user in users)
            {
                user.SendMessage(message);
            }
        }
        /// <summary>
        /// 获取指定Channel
        /// </summary>
        /// <param name="name">Channel名</param>
        /// <returns>指定的Channel实例</returns>
        public Channel GetChannel(string name)
        {
            if (this.channelList .ContainsKey  (name))
            {
                return this.channelList[name];
            }
            return null;
        }
        /// <summary>
        /// 获取指定Channel
        /// </summary>
        /// <param name="name">Channel名</param>
        /// <returns>指定的Channel实例</returns>
        public T GetChannel<T>(string name)where T :Channel 
        {
            if (this.channelList.ContainsKey(name))
            {
                return this.channelList[name] as T;
            }
            return null;
        }
        [MethodImpl(MethodImplOptions.Synchronized)]
        public Channel CreateNewChannel(string name)
        {
            var channel = this.CreateChannel(name);
            if (channel .OperationDone ==false )
            {
                this.channelList.Add(name, channel.AdditionalData );
               
            }
            
            if (this.ChannelCreated!=null )
            {
                this.ChannelCreated(this, new ChannelEventArgs () {  ChannelName = name });
            }
            return channel.AdditionalData ;
        }
        [MethodImpl(MethodImplOptions.Synchronized)]
        public T CreateNewChannel<T>(string name) where T : Channel
        {
            var channel = this.CreateChannel(name);
            if (channel.OperationDone == false)
            {
                this.channelList.Add(name, channel.AdditionalData);
            }

            if (this.ChannelCreated != null)
            {
                this.ChannelCreated(this, new ChannelEventArgs() { ChannelName = name });
            }
            return channel.AdditionalData as T;
        }
        /// <summary>
        /// 创建一个Channel，如果已经存在同名Channel就返回该Channel，否则返回一个新的Channel
        /// </summary>
        /// <param name="name"></param>
        /// <returns>返回的ResultInfo中的OperationDone表示是否存在同名Channel</returns>
        protected  abstract ResultInfo <Channel  > CreateChannel(string name);
        public abstract void OnStart();
        public abstract void OnStop();
    }
}
